package com.koron.ticket.operation.service.imp;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.StringUtils;
import com.koron.ticket.enums.ApprovEnum;
import com.koron.ticket.enums.OperationGroupEnum;
import com.koron.ticket.enums.OperationTypeEnum;
import com.koron.ticket.enums.SubmitEnum;
import com.koron.ticket.operation.bean.*;
import com.koron.ticket.operation.bean.dto.*;
import com.koron.ticket.operation.bean.excelbean.InspectAppOrg;
import com.koron.ticket.operation.bean.excelbean.OperationStepBaseTemplate;
import com.koron.ticket.operation.bean.excelbean.OperationStepTemplate;
import com.koron.ticket.operation.bean.excelbean.OperationStepcChildrenTemplate;
import com.koron.ticket.operation.bean.print.OperationFactoryBean;
import com.koron.ticket.operation.mapper.*;
import com.koron.ticket.operation.service.OperationService;
import lombok.extern.slf4j.Slf4j;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.koron.common.core.util.StringUtils.isNotEmpty;
import static com.koron.common.core.util.StringUtils.isNull;

@Slf4j
@Service
public class OperationServiceImp implements OperationService {


    private static String yyMMtime() {
        return LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE).substring(2, 6);
    }

    private final static Integer NEXT = Integer.valueOf(1);


    /**
     * 保存或更新
     *
     * @param factory
     * @param bean
     * @return
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public synchronized String saveOrUpdate(SessionFactory factory, OperationTicketBean bean) {
        String id = CodeTools.getCode32();
        OperationTicketMapper mapper = factory.getMapper(OperationTicketMapper.class);//操作票
        OperationStepMapper stepmapper = factory.getMapper(OperationStepMapper.class);//操作步骤
        OperationWorkMapper operationworkmapper = factory.getMapper(OperationWorkMapper.class);//操作票对应工作票
        if (OperationGroupEnum.SecurityTicket.getValue().equals(bean.getGroup())){
            if (StringUtils.isNotEmpty(bean.getWorkCodeSplit())){
                mapper.updateByPrimaryKeySelective(bean);
                OperationWorkJoin(bean, operationworkmapper, bean.getId());//操作票、工作票关联
            }else {
                bean.setDeptId(getCurrUser().getOrgId());
                bean.setStatus(0);//未审核
                mapper.insertSelective(bean);
                generateDet(bean, stepmapper, bean.getId());//生成 操作步骤明细
            }
        }
        else if (StringUtils.isEmpty(bean.getId())) {
            bean.setId(id);
            bean.setDeptId(getCurrUser().getOrgId());
            bean.setStatus(0);//未审核

            mapper.insertSelective(bean);

            generateDet(bean, stepmapper, id);//生成 操作步骤明细

            OperationWorkJoin(bean, operationworkmapper, id);//操作票、工作票关联


        } else {
            mapper.updateByPrimaryKeySelective(bean);

            stepmapper.deleteByOperationId(bean.getId());
            generateDet(bean, stepmapper, bean.getId());//生成 安全措施明细、操作步骤明细

            operationworkmapper.deleteByOperationId(bean.getId());//先 删除工作票操作票 关系
            OperationWorkJoin(bean, operationworkmapper, bean.getId());


            //执行信息
            OperationExecuteBean executemsg = bean.getExecutemsg();
            saveExecutMsg(factory, executemsg);
        }
        // 受令人
        ycSaveSLR(factory, bean.getOperationuserlist(), bean.getId());

        return bean.getId();
    }

    /**
     * 操作票号生成
     *
     * @param bean
     * @return
     */
    private String geCodeNo(OperationTicketBean bean) {
        String codeNo = "";
        String middleStr = "";
        if (bean.getType().equals(OperationTypeEnum.TYPE_A.getValue())) {
            middleStr = "_A";   //A票
            Assert.isFalse(StrUtil.isBlank(bean.getWorkCodeSplit()), "请补充工作票号");
        }
        if (bean.getType().equals(OperationTypeEnum.TYPE_B.getValue())) {
            middleStr = "_B";   //B票
            Assert.isFalse(StrUtil.isBlank(bean.getWorkCodeSplit()), "请补充工作票号");
        }
        if (StrUtil.isNotBlank(bean.getWorkCodeSplit())) {
            codeNo = CodeTools.getCurrAppCodeNoWithNoDate(bean.getDeptId() + bean.getWorkCodeSplit().split(",")[0] + middleStr, "y", 2);
            codeNo = StringUtils.remove(codeNo, bean.getDeptId());
        }
        if (bean.getType().equals(OperationTypeEnum.OPERATION_NO_WORK.getValue())) {
            codeNo = StringUtils.remove(CodeTools.getCodeNo("无工作票号" + bean.getDeptId(), "yy", 4), "无工作票号" + bean.getDeptId());
        }
        return codeNo;
    }

    /**
     * 操作票、工作票关联
     *
     * @param bean
     * @param operationworkmapper
     * @param id
     */
    private void OperationWorkJoin(OperationTicketBean bean, OperationWorkMapper operationworkmapper, String id) {
        if (isNotEmpty(bean.getWorkIds())) {
            List<OperationWorkBean> owlist = new ArrayList<>();
            bean.getWorkIds().stream().forEach(w -> {
                owlist.add(new OperationWorkBean(CodeTools.getCode32(), id, w.getWorkId(), w.getWorkCode(), new Date()));
            });
            operationworkmapper.batchInsert(owlist);
        }
    }

    /**
     * 保存执行信息
     *
     * @param factory
     * @param executemsg
     */
    private void saveExecutMsg(SessionFactory factory, OperationExecuteBean executemsg) {
        if (StringUtils.isNotNull(executemsg)) {
            OperationExecuteMapper executeMapper = factory.getMapper(OperationExecuteMapper.class);
            if (StringUtils.isEmpty(executemsg.getId())) {
                executemsg.setId(CodeTools.getCode32());
                executeMapper.insertSelective(executemsg);
            } else {
                executeMapper.updateByPrimaryKeySelective(executemsg);
            }
        }
    }

    /**
     * 保存：安全措施明细、操作步骤
     *
     * @param bean
     * @param stepmapper
     * @param id
     */
    private void generateDet(OperationTicketBean bean, OperationStepMapper stepmapper, String id) {
        //操作步骤明细
        if (isNotEmpty(bean.getStepdet())) {
            bean.getStepdet().stream().forEach(b -> {
                String steid = CodeTools.getCode32();
                b.setId(steid);
                b.setOperationId(id);
                //子步骤不为空
                if (StringUtils.isNotEmpty(b.getChildren())) {
                    b.getChildren().stream().forEach(d -> {
                        d.setParentId(steid);
                        d.setId(CodeTools.getCode32());
                        d.setOperationId(id);
                    });
                    stepmapper.batchInsert(b.getChildren());//写入子步骤
                }
            });
            if (OperationGroupEnum.SecurityTicket.getValue().equals(bean.getGroup())){
                stepmapper.insertOrUpdateBatch(bean.getStepdet());
            }else {
                stepmapper.batchInsert(bean.getStepdet());//写入主步骤
            }
        }
    }

    /**
     * 受令人
     *
     * @param factory
     * @param list
     */
    private void ycSaveSLR(SessionFactory factory, List<OperationUser> list, String operationId) {
        OperationUserMapper mapper = factory.getMapper(OperationUserMapper.class);
        if(CollUtil.isNotEmpty(list)) {
            mapper.deleteByOperationId(operationId);
            list.stream().forEach(l->{
                l.setOperationId(operationId);
                l.setCreateTime(new Date());
                l.setId(CodeTools.getCode32());
            });
            mapper.batchInsert(list);
        }
    }

    /**
     * 删除
     *
     * @param factory
     * @param id
     * @return
     */
    @Override
    @TaskAnnotation("deleteById")
    public int deleteById(SessionFactory factory, String id) {
        OperationTicketMapper mapper = factory.getMapper(OperationTicketMapper.class);
        OperationTicketBean bean = new OperationTicketBean();
        bean.setId(id);
        bean.setDeleteFlag(1);
        return mapper.updateDeleteFlagById(bean);
    }


    /**
     * 详情
     *
     * @param factory
     * @param id
     * @return
     */
    @Override
    @TaskAnnotation("queryById")
    public OperationTicketDetailsDto queryById(SessionFactory factory, String id) {
        OperationTicketMapper mapper = factory.getMapper(OperationTicketMapper.class);//操作票
        OperationStepMapper stepmapper = factory.getMapper(OperationStepMapper.class);//操作步骤
        OperationExecuteMapper executemapper = factory.getMapper(OperationExecuteMapper.class);//执行信息
        OperationWorkMapper operationWorkMapper = factory.getMapper(OperationWorkMapper.class);//操作票工作票 关联
        OperationUserMapper operationusermapper = factory.getMapper(OperationUserMapper.class);//受令人 关联

        OperationTicketBean bean = mapper.selectByPrimaryKey(id);
        OperationExecuteBean executemsg = executemapper.queryByOperationId(id);
        List<OperationStepBean> steptemlist = stepmapper.queryAllByOperationId(id);
        List<OperationWorkBean> operWorklist = operationWorkMapper.queryByOperationId(id);

        bean.setWorkCodeSplit(operWorklist.stream().map(OperationWorkBean::getWorkCode).collect(Collectors.joining(",")));//工作票号逗号分割
        List<OperationWorkDto> wlist = operWorklist.stream().map(w -> {
            return new OperationWorkDto(w.getWorkId(), w.getWorkCode());
        }).collect(Collectors.toList());//获取安全措施（工作票id，工作票code）集合
        bean.setWorkIds(wlist);

        if (StringUtils.isNotEmpty(steptemlist)) {
            List<OperationStepBean> steplist = steptemlist.stream().filter(st -> StringUtils.isEmpty(st.getParentId())).collect(Collectors.toList());

            steplist.stream().forEach(st -> {
                List<OperationStepBean> det = steptemlist.stream().filter(tem -> st.getId().equals(tem.getParentId())).collect(Collectors.toList());
                st.setChildren(det);
            });

            bean.setStepdet(steplist);
        }
        bean.setExecutemsg(executemsg);
        OperationTicketDetailsDto dto = new OperationTicketDetailsDto();
        BeanUtils.copyProperties(bean, dto);
        List<OperationUser> operationUsers = operationusermapper.queryByOperationId(id);
        dto.setOperationuserlist(operationUsers);
        return dto;
    }

    /**
     * 列表
     *
     * @param factory
     * @param queryBean
     * @return
     */
    @Override
    @TaskAnnotation("queryList")
    public PageInfo queryList(SessionFactory factory, OperationTicketQueryDto queryBean) {
        queryBean.setUnits(getCurrUserOrgIds());
        PageHelper.startPage(queryBean.getPage(), queryBean.getPageSize(), queryBean.getOrderBy());
        OperationTicketMapper mapper = factory.getMapper(OperationTicketMapper.class);
        List<OperationTicketQueryDto> vos = mapper.queryList(queryBean);
        return new PageInfo(vos);
    }


    /**
     * 交接班 远程调用 接口列表 无分页
     *
     * @param factory
     * @param dto
     * @return
     */
    @Override
    @TaskAnnotation("queryListNoPage")
    public List<OperationTicketQueryDto> queryListNoPage(SessionFactory factory, OperationTicknNewsDto dto) {
        OperationTicketMapper mapper = factory.getMapper(OperationTicketMapper.class);
        List<OperationTicketQueryDto> dtolist = mapper.queryListNoPage(dto);
        return dtolist;
    }

    /**
     * 审核
     * 操作票状态
     * 0	新增
     * 1	已自审
     * 2	已复审
     * 3	已归档
     * -1	已作废
     *
     * @param factory
     * @param dto
     * @return
     */
    @Override
    @TaskAnnotation("submit")
    public int submit(SessionFactory factory, OperationSubmitDto dto) {
        OperationTicketMapper mapper = factory.getMapper(OperationTicketMapper.class);
        OperationTicketBean bean = mapper.selectByPrimaryKey(dto.getId());
        Integer updatedStatus = bean.getStatus();

        //通过
        if (dto.getSubmitType() == SubmitEnum.ADOPT.getIntValue()) {
            updatedStatus = updatedStatus + NEXT;
        }
        //不通过、撤回
        if (dto.getSubmitType() == SubmitEnum.FAIL.getIntValue()) {
            updatedStatus = Integer.valueOf(ApprovEnum.ADD.getValue());
        }
        //作废
        if (dto.getSubmitType() == SubmitEnum.TOCANCEL.getIntValue()) {
            updatedStatus = Integer.valueOf(ApprovEnum.VOIDED.getValue());
        }
        //复审 补充 执行信息
        if (updatedStatus.toString().equals(ApprovEnum.REVIEWED.getValue()) && dto.getSubmitType().equals(SubmitEnum.ADOPT.getIntValue())) {
            OperationExecuteMapper executeMapper = factory.getMapper(OperationExecuteMapper.class);
            OperationExecuteBean executeBean = new OperationExecuteBean(dto);
            executeBean.setId(CodeTools.getCode32());
            executeBean.setWorker(bean.getChecker());
            executeBean.setWorkerName(bean.getCheckerName());
            executeBean.setStartTime(new Date());
            executeMapper.insertSelective(executeBean);
            if (StrUtil.isBlank(bean.getCode())) {
                //复审 补充票号
                List<OperationWorkBean> operworklist = factory.getMapper(OperationWorkMapper.class).queryByOperationId(bean.getId());
                if (CollUtil.isNotEmpty(operworklist)) {
                    String workcode = operworklist.stream().map(OperationWorkBean::getWorkCode).collect(Collectors.joining(","));
                    bean.setWorkCodeSplit(workcode);
                }
                String codeNo = geCodeNo(bean);
                mapper.updateCodeById(codeNo, bean.getId());
            }
        }
        return mapper.updateStatusById(updatedStatus, dto.getId());
    }

    /**
     * 自审
     *
     * @param factory
     * @param userName 账号
     * @param password 密码
     * @return
     */
    @Override
    @TaskAnnotation("checkSign")
    public Boolean checkSign(SessionFactory factory, String userName, String password) {

        AppUserBean appUserBean = factory.getMapper(OperationTicketMapper.class).queryUserByAcc(userName);
        if (isNull(appUserBean)) {
            return false;
        }
        String passwordMd5 = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!passwordMd5.equals(appUserBean.getPassword())) {
            return false;
        }
        return true;
    }

    @Override
    public List<OperationStepBean> excelImpDetStepdet() {
        return null;
    }


    @Override
    @TaskAnnotation("checkForDuplicates")
    public OperationStepBaseTemplate checkForDuplicates(SessionFactory factory, List<OperationStepTemplate> checklist) {

        List<InspectAppOrg> inspect = factory.getMapper(OperationTicketMapper.class).queryAppOrg();

        Set<String> detpset = inspect.stream().map(InspectAppOrg::getName).collect(Collectors.toSet());

        for (OperationStepTemplate impdata : checklist) {
            List<OperationStepcChildrenTemplate> childrenDet = impdata.getChildren();
            for (OperationStepcChildrenTemplate impdet : childrenDet) {
                OperationStepBaseTemplate childerntemplate = (OperationStepBaseTemplate) impdet;
                if (concreteErrMsg(detpset, childerntemplate)) {
                    return childerntemplate;            //检查子步骤
                }
            }
        }

        return new OperationStepBaseTemplate();
    }

    /**
     * 导入 操作票excel具体步骤 具体错误信息
     *
     * @param detpset  所有部门信息
     * @param template 公用模板
     * @return
     */
    private boolean concreteErrMsg(Set<String> detpset, OperationStepBaseTemplate template) {
        if (isNotEmpty(template.getDeptName())) {
            if (!detpset.contains(template.getDeptName())) {
                int i = template.getRowNum() + 1;
                template.setErrorMsg("第" + i + "行: 受领单位" + template.getDeptName() + "不存在");
                return true;
            }
        }

        return false;
    }

    /**
     * Object 转换 可用 泛型
     *
     * @param obj   源
     * @param clazz 目标类型
     * @param <T>   目标
     * @return
     */
    public static <T> List<T> castList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<T>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }


    @Override
    @TaskAnnotation("recordContext")
    public int recordContext(SessionFactory factory, OperationDescBean bean) {
        OperationDescMapper mapper = factory.getMapper(OperationDescMapper.class);
        if (StringUtils.isNotEmpty(bean.getId())) {
            bean.setUpdateBy(getCurrUser().getAccount());
            bean.setUpdateByName(getCurrUser().getName());
            bean.setUpdateTime(new Date());
            return mapper.updateContextByOperationId(bean);
        } else {
            bean.setId(CodeTools.getCode32());
            bean.setCreateBy(getCurrUser().getAccount());
            bean.setCreateByName(getCurrUser().getName());
            bean.setCreateTime(new Date());
            return mapper.insertSelective(bean);
        }
    }

    @Override
    @TaskAnnotation("printOperation")
    public List<OperationFactoryBean> printOperation(SessionFactory factory, String id) {

        List<OperationFactoryBean> list = new ArrayList<>();
        OperationTicketBean bean = factory.getMapper(OperationTicketMapper.class).selectByPrimaryKey(id);
        if (StringUtils.isNull(bean)) {
            list.add(new OperationFactoryBean());//可以获取空的操作票
            return list;
        }

        OperationExecuteBean executeBean = Optional.ofNullable(factory.getMapper(OperationExecuteMapper.class).queryByOperationId(bean.getId())).orElse(new OperationExecuteBean());
        OperationDescBean desceban = Optional.ofNullable(factory.getMapper(OperationDescMapper.class).queryByOperationId(bean.getId())).orElse(new OperationDescBean());
        List<OperationStepBean> stepList = Optional.ofNullable(factory.getMapper(OperationStepMapper.class).queryAllByOperationId(bean.getId())).orElse(new ArrayList<>());
        List<OperationUser> userlist = Optional.ofNullable(factory.getMapper(OperationUserMapper.class).queryByOperationId(bean.getId())).orElse(new ArrayList<>());
        //因为需求可以求打印空的操作票
        if (StringUtils.isEmpty(stepList)) {
            list.add(new OperationFactoryBean());//可以获取空的操作票
            return list;
        }
        stepList.stream().filter(s -> StringUtils.isNotEmpty(s.getItem())).forEach(st -> {
            OperationFactoryBean printBean = new OperationFactoryBean();
            printBean.setItem(st.getItem());
            printBean.setDutyManName(executeBean.getDutyManName());
            printBean.setGuardianName(executeBean.getGuardianName());
            printBean.setStartTime(DateUtil.format(executeBean.getStartTime(), "yyyy年MM月dd日HH时mm分"));
            printBean.setEndTime(DateUtil.format(executeBean.getEndTime(), "yyyy年MM月dd日HH时mm分"));

            if (bean.getType().equals(OperationTypeEnum.TYPE_A.getValue())) {
                printBean.setType("A");
            } else if (bean.getType().equals(OperationTypeEnum.TYPE_B.getValue())) {
                printBean.setType("B");
            }

            printBean.setCode(bean.getCode());
            printBean.setRemarks(StringUtils.trim(st.getRemarks()));//安全步骤备注
            printBean.setContext(StringUtils.trim(bean.getContext()));
            // 电气倒闸发令人
            printBean.setIrritating(bean.getIrritating());
            // 电气倒闸受令人
            printBean.setIrritatingNameStr(userlist.stream().map(OperationUser::getUserName).collect(Collectors.joining(",")));
            // 电气倒闸法令时间
            printBean.setCommand(DateUtil.format(bean.getCommand(), "yyyy年MM月dd日HH时mm分"));
            printBean.setWorkerName(executeBean.getWorkerName());

            printBean.setPublishTime(DateUtil.format(st.getPublishTime(), "yyyy年MM月dd日HH时mm分"));
            printBean.setReportTime(DateUtil.format(st.getReportTime(), "yyyy年MM月dd日HH时mm分"));
            printBean.setAcceptManName(st.getAcceptManName());
            printBean.setReportManName(st.getReportManName());
            printBean.setDeptName(st.getDeptName());
            printBean.setOpdeptName(bean.getDeptName());//操作票开票单位
            printBean.setOpremarks(executeBean.getDesc());//操作票备注
            list.add(printBean);
        });

        return list;
    }
}
